#include "ws2812_pwm.h"

uint8_t ws2812_buffer[WS2812_BUFFER_SIZE];

static volatile uint16_t timer_overflows = 0;

const uint32_t TIME_UNIT_FREQ_HZ = 800000U;
const uint16_t TIMER_RELOAD = 10 - 1;


#define CODE_0      (3)
#define CODE_1      (7)
#define RESET_LEN    (60)

static void timer_config(void)
{
    LL_TIM_InitTypeDef timer_initpara = {0};
    LL_TIM_OC_InitTypeDef timer_ocpara = {0};

    /* enable timer2 clock */
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);
    LL_TIM_DeInit(TIM3);
    
    /* timer base configure */
    timer_initpara.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
    timer_initpara.CounterMode = LL_TIM_COUNTERMODE_UP;
    timer_initpara.Prescaler = (SystemCoreClock / (TIME_UNIT_FREQ_HZ * (TIMER_RELOAD + 1))) - 1;
    timer_initpara.Autoreload = TIMER_RELOAD;
    timer_initpara.RepetitionCounter = 0;
    LL_TIM_Init(TIM3, &timer_initpara);
    LL_TIM_DisableARRPreload(TIM3);
    LL_TIM_EnableARRPreload(TIM3);
    
    timer_ocpara.OCMode = LL_TIM_OCMODE_PWM1;
    timer_ocpara.OCState = LL_TIM_OCSTATE_ENABLE;
    timer_ocpara.CompareValue = 0;
    timer_ocpara.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
    timer_ocpara.OCIdleState = LL_TIM_OCIDLESTATE_HIGH;
    LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH2, &timer_ocpara);
    LL_TIM_OC_DisablePreload(TIM3, LL_TIM_CHANNEL_CH2);  
}

void gpio_config(void)
{
    LL_GPIO_InitTypeDef gpio_initpara = {0};

    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);

    gpio_initpara.Pin = LL_GPIO_PIN_5;
    gpio_initpara.Mode = LL_GPIO_MODE_ALTERNATE;
    gpio_initpara.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    gpio_initpara.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    gpio_initpara.Pull = LL_GPIO_PULL_NO;
    gpio_initpara.Alternate = LL_GPIO_AF_6;
    
    LL_GPIO_Init(GPIOB, &gpio_initpara);
}

static void dma_config(void)
{
    LL_DMA_InitTypeDef dma_initpara = {0};

    /* enable dma1 clock */
    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
    
    /* DMA1 CH2 timer2 update*/
    LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_5);
    dma_initpara.PeriphOrM2MSrcAddress = (uint32_t)&TIM3->CCR2;
    dma_initpara.MemoryOrM2MDstAddress = (uint32_t)&ws2812_buffer;
    dma_initpara.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
    dma_initpara.Mode = LL_DMA_MODE_NORMAL;
    dma_initpara.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
    dma_initpara.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
    dma_initpara.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
    dma_initpara.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
    dma_initpara.NbData = 0;
    dma_initpara.Priority = LL_DMA_PRIORITY_HIGH;
    LL_DMA_Init(DMA1, LL_DMA_CHANNEL_5, &dma_initpara);
    
    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_5);

    /* IMPORTANT: dma channel map */
    LL_SYSCFG_SetDMARemap(DMA1, LL_DMA_CHANNEL_5, LL_SYSCFG_DMA_MAP_TIM3_CH2); 
    
    LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_5);
    NVIC_EnableIRQ(DMA1_Channel5_IRQn);
    NVIC_SetPriority(DMA1_Channel5_IRQn, 0);
}


void DMA1_Channel5_IRQHandler(void)
{
    if (LL_DMA_IsActiveFlag_TC5(DMA1))
    {
        LL_DMA_ClearFlag_TC5(DMA1);
        LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_5);
        LL_TIM_DisableCounter(TIM3);
//        printf("DMA transfer complete, remaining length: %lu\n", LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_5));
    }
}

int ws2812_init_pwm(void) {
    
    memset(ws2812_buffer, 7, WS2812_BUFFER_SIZE);

    timer_config();
    gpio_config();
    dma_config();
//    LL_TIM_EnableAllOutputs(TIM3);
//    LL_TIM_EnableCounter(TIM3);
    return 0;
}

void ws2812_send_data_pwm(uint32_t buffersize)
{

    while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_5)!=0);
    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_5, buffersize);
    printf("DMA SET length: %lu\n", LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_5));
    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_5);
    LL_TIM_EnableDMAReq_CC2(TIM3);

    LL_TIM_EnableCounter(TIM3);
}
